/*
 * Copyright 2025 The JA-SIG Collaborative. All rights reserved.
 * distributed with this file and available online at
 */
package com.lap.auth.application.user;

import com.lap.auth.application.user.assembler.UserAssembler;
import com.lap.auth.application.user.command.CreateUserCmd;
import com.lap.auth.application.user.command.UpdateUserCmd;
import com.lap.auth.application.user.dto.*;
import com.lap.auth.application.user.repository.UserQueryRepository;
import com.lap.auth.domain.user.User;
import com.lap.auth.domain.user.repository.UserRepository;
import com.lap.auth.domain.user.repository.UserRoleRepository;
import com.lap.auth.domain.user.service.UserValidator;
import com.lap.framework.common.dto.page.PageResponse;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * 系统管理员应用服务层
 *
 * <ol>
 *   <li>不做业务规则，只做技术规则（加密/解密是技术实现，业务不关注）
 *   <li>取数，赋值，对象转换在这层执行
 *   <li>命名有规范，动词+领域名字
 *   <li>如查询比较多，建议分开领域QueryAppService,如:分页查询，分页查询返回关联字段
 * </ol>
 */
@RequiredArgsConstructor
@Service
public class UserAppService {

  private final UserAssembler userAssembler;
  private final UserValidator userValidator;
  private final UserRepository userRepository;
  private final UserRoleRepository userRoleRepository;
  private final UserQueryRepository userQueryRepository;
  private final PasswordEncoder passwordEncoder;
  private final ApplicationEventPublisher publisher;

  public PageResponse<UserWithOrgDto> queryUsersWithOrgByPage(QueryUserPageQuery query) {
    return userQueryRepository.findWithPostPaged(query);
  }

  public PageResponse<UserSimpleDto> querySimplesByPage(QueryUserSimplePageQuery query) {
    return userQueryRepository.findSimplePaged(query);
  }

  public List<UserSimpleDto> queryUsersByIds(GetUserByIdsQuery query) {
    return userQueryRepository.findSimpleByIds(query.getIdSet());
  }

  public UserSimpleDto getSimpleUserById(Integer id) {
    return userQueryRepository.findSimpleById(id);
  }

  public Integer createUser(CreateUserCmd cmd) {
    userValidator.validateUserNameUnique(cmd.getUserName());
    User user = userAssembler.toDomain(passwordEncoder.encode(cmd.getPassword()), cmd);
    user.createEvent();

    userRepository.save(user);
    publishEvents(user);
    return user.getId();
  }

  public void updateUser(Integer id, UpdateUserCmd cmd) {
    User snapshot = userValidator.assertUserExists(id);
    User user = userAssembler.toDomain(id, snapshot.getPassword(), snapshot.getType(), cmd);
    user.updateEvent();

    userRepository.save(user);
    publishEvents(user);
  }

  public void toggleUserStatus(Integer id, String remark, Integer version, boolean enable) {
    User user = userValidator.assertUserExists(id);
    if (enable) {
      user.enable(remark, version);
    } else {
      user.disable(remark, version);
    }

    userRepository.save(user);
    publishEvents(user);
  }

  public void changePassword(Integer id, String password) {
    var snapshot = userValidator.assertUserExists(id);
    snapshot.changePassword(passwordEncoder.encode(password));

    userRepository.save(snapshot);
    publishEvents(snapshot);
  }

  @Transactional(rollbackFor = Exception.class)
  public void deleteUser(Integer id) {
    userValidator.assertUserExists(id);
    userRepository.deleteById(id);
    userRoleRepository.deleteByUserId(id);
  }

  private void publishEvents(User user) {
    user.getEvent().forEach(publisher::publishEvent);
    user.clearEvents();
  }
}
